package com.bestow.dox4j.component.handler;

import com.bestow.basic.domain.HlsDocFileParaTable;
import com.bestow.dox4j.Docx4jConstants;
import com.bestow.dox4j.IBookmarkHandler;
import org.docx4j.UnitsOfMeasurement;
import org.docx4j.XmlUtils;
import org.docx4j.jaxb.Context;
import org.docx4j.model.structure.SectionWrapper;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.math.BigInteger;
import java.util.*;

/**
 * 生成表格的插件
 * Created by FJM on 2017/4/17.
 */
@Component
public class TableBookmarkHandler implements IBookmarkHandler {

    protected static Logger log = LoggerFactory.getLogger(TableBookmarkHandler.class);
    private static ObjectFactory factory = Context.getWmlObjectFactory();

    @Override
    public String getType() {
        return Docx4jConstants.BOOKMARK_TYPE_TABLE;
    }

    @Override
    public void process(WordprocessingMLPackage wordMLPackage, List<HlsDocFileParaTable> tableColumnList, List<HashMap> bookMarkValueList, String bookmark, Map<String, Object> params) throws Exception {
        replaceTableBookmarkContents(wordMLPackage, tableColumnList, bookMarkValueList, bookmark);
    }


    public void replaceTableBookmarkContents(WordprocessingMLPackage wordMLPackage, List<HlsDocFileParaTable> tableColumnList, List<HashMap> sqlData, String bookmark) throws Exception {
        MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
        Document wmlDocumentEl = documentPart.getJaxbElement();
        Body body = wmlDocumentEl.getBody();
        if (sqlData != null && sqlData.size() > 0) {
            replaceTableBookmarkContents(wordMLPackage, body.getContent(), tableColumnList, sqlData, bookmark);
        }

    }

    private void replaceTableBookmarkContents(WordprocessingMLPackage wordMLPackage, List<Object> content, List<HlsDocFileParaTable> tableClomnList, List<HashMap> sqlData, String bookmark) throws Exception {

        log.info("开始进行表格添加" );
        List<SectionWrapper> sections = wordMLPackage.getDocumentModel()
                .getSections();
        SectPr sectPr = sections.get(sections.size() - 1).getSectPr();
        int width = sectPr.getPgSz().getW().intValue();
        int cellWidthValue = 0;
        float indwidth = 0;
        int tablewidth = 0;
        int tableBorder = 0;
        Tr headtr = null;

        log.info("将要开始进行数据分组" );

        //对数据进行分组、过滤
        Map<String, List<HashMap>> groups = makeGroups(tableClomnList, sqlData, true, true);

        if (groups == null || groups.size() < 1) {
            log.error("bookmarkValue分组数据出错！" );
            return;
        }

        List<Object> tbls = new ArrayList<Object>();

        log.info("开始生成表头数据" );

        for (HlsDocFileParaTable tableChild : tableClomnList) {
            Tc tc = factory.createTc();
            TcPr tcPr = factory.createTcPr();
            P p = factory.createP();
            PPr PPr = factory.createPPr();
            ParaRPr RPr = factory.createParaRPr();
            org.docx4j.wml.RPr RPr1 = factory.createRPr();
            RFonts RFonts = factory.createRFonts();
            String fontFamily = null;
            String fontSize = null;
            indwidth = 1f;//tableChild.getFloat("ind_width")表中暂无此字段
            tablewidth = 0;//tableChild.getInt("table_width")表中暂无此字段
            tableBorder = 1;//tableChild.getInt("border", 1)表中暂无此字段
            RFonts.setHint(STHint.EAST_ASIA);
            RFonts.setEastAsia(fontFamily);
            RFonts.setAscii(fontFamily);
            RFonts.setHAnsi(fontFamily);
            if (fontSize != null) {
                HpsMeasure sz = new HpsMeasure();
                sz.setVal(new BigInteger(fontSize));
                RPr1.setSz(sz);
                RPr1.setSzCs(sz);
                RPr.setSz(sz);
                RPr.setSzCs(sz);

            }
            RPr.setRFonts(RFonts);
            RPr1.setRFonts(RFonts);
            PPr.setRPr(RPr);
            String align = tableChild.getAlignment();
            if (align != null) {
                Jc jc = factory.createJc();
                jc.setVal(JcEnumeration.fromValue(align.toLowerCase()));
                PPr.setJc(jc);
            }
            tc.setTcPr(tcPr);
            p.getContent().add(PPr);
            tc.getContent().add(p);
            TblWidth cellWidth = factory.createTblWidth();
            tcPr.setTcW(cellWidth);
            cellWidth.setType("dxa" );
            cellWidthValue = (int) (tableChild.getColumnWidth() != null ? tableChild.getColumnWidth() : width / tableClomnList.size());
            cellWidth.setW(BigInteger.valueOf(cellWidthValue));
            R run = factory.createR();
            run.setRPr(RPr1);
            Text t = factory.createText();
            t.setValue(tableChild.getColumnDesc());
            System.out.println("找到一个表头：" + tableChild.getColumnDesc());
            run.getContent().add(t);
            p.getContent().add(run);
            if (bookmark.lastIndexOf("_NOTITLE" ) == -1) {
                if (headtr == null) {
                    headtr = factory.createTr();
                    /*tbl.getContent().add(headtr);*/
                }
                headtr.getContent().add(tc);
            }
        }
        System.out.println("表头生成完成。" );

        System.out.println("开始生成表格数据：" );
        Iterator<Map.Entry<String, List<HashMap>>> iterator = groups.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, List<HashMap>> next = iterator.next();
            List<HashMap> value = next.getValue();
            System.out.println("增加了一张表格" );

            //创建一个table
            Tbl tbl = makeTbl();

            //添加表头
            tbl.getContent().add(headtr);

            if (value != null) {
                for (HashMap valueChild : value) {
                    Tr tr = factory.createTr();
                    tbl.getContent().add(tr);
                    System.out.println("增加了一行数据" );
                    for (HlsDocFileParaTable tableChild : tableClomnList) {
                        Tc tc = factory.createTc();
                        TcPr tcPr = factory.createTcPr();
                        P p = factory.createP();
                        PPr PPr = factory.createPPr();
                        ParaRPr RPr = factory.createParaRPr();
                        org.docx4j.wml.RPr RPr1 = factory.createRPr();
                        RFonts RFonts = factory.createRFonts();
                        String fontFamily = null;//tableChild.getString("font_family")
                        String fontSize = null;//tableChild.getString("font_size")
                        RFonts.setHint(STHint.EAST_ASIA);
                        RFonts.setEastAsia(fontFamily);
                        RFonts.setAscii(fontFamily);
                        RFonts.setHAnsi(fontFamily);
                        if (fontSize != null) {   //如果fontSize为空，则不做处理，使用docx4j的默认值
                            HpsMeasure sz = new HpsMeasure();
                            sz.setVal(new BigInteger(fontSize));
                            RPr.setRFonts(RFonts);
                            RPr1.setRFonts(RFonts);
                            RPr1.setSz(sz);
                            RPr1.setSzCs(sz);
                            RPr.setSz(sz);
                            RPr.setSzCs(sz);
                        }
                        PPr.setRPr(RPr);
                        String align = tableChild.getAlignment();
                        if (align != null) {
                            Jc jc = factory.createJc();
                            jc.setVal(JcEnumeration.fromValue(align.toLowerCase()));
                            PPr.setJc(jc);
                        }
                        tc.setTcPr(tcPr);
                        p.getContent().add(PPr);
                        tc.getContent().add(p);
                        TblWidth cellWidth = factory.createTblWidth();
                        tcPr.setTcW(cellWidth);
                        cellWidth.setType("dxa" );
                        cellWidthValue = (int) (tableChild.getColumnWidth() != null ? tableChild.getColumnWidth() : width / tableClomnList.size());
                        cellWidth.setW(BigInteger.valueOf(cellWidthValue));
                        R run = factory.createR();
                        run.setRPr(RPr1);
                        Text t = factory.createText();
                        Object o = valueChild.get(tableChild.getColumnName());
                        String columnNameValue = o != null ? o.toString() : "";
                        t.setValue(columnNameValue);
                        System.out.println("增加单元格：" + columnNameValue);
                        run.getContent().add(t);
                        p.getContent().add(run);
                        tr.getContent().add(tc);
                    }
                }
            }

            //给table加上边框
            addBorders(tbl, indwidth, tablewidth, tableBorder);
            tbls.add(tbl);

            //防止table连接在一块，加上换行
            tbls.add(factory.createP());
        }
        System.out.println("表格生成完毕共：" + tbls.size());

        // 找到对应书签的父元素（P元素）
        final String xpath = "//w:bookmarkStart[@w:name='" + bookmark + "']/..";
        List<Object> objects = wordMLPackage.getMainDocumentPart().getJAXBNodesViaXPath(xpath, false);
        if (objects != null && objects.size() != 0) {
            System.out.println("找到书签对应的位置" );
            P para = (P) XmlUtils.unwrap(objects.get(0));
            R r = factory.createR();
            r.getContent().addAll(tbls);
            System.out.println("所有表格准备完成" );
            para.getContent().add(r);
            System.out.println("表格已插入到文档中" );
        } else {
            System.out.println("没有找到对应的书签：" + bookmark);

        }
        System.out.println("本次替换结束" );

    }

    private Tbl makeTbl() {
        Tbl tbl = factory.createTbl();
        TblPr tblPr = factory.createTblPr();
        CTTblPrBase.TblStyle tblStyle = factory.createCTTblPrBaseTblStyle();
        tblStyle.setVal("TableGrid");
        tblPr.setTblStyle(tblStyle);
        tbl.setTblPr(tblPr);
        return tbl;
    }

    /**
     * 将数据分组并筛选，当筛选字段为N，且开启过滤时，不输出该条记录
     *
     * @param sqlData          表格值内容
     * @param tableClomnList   表格头设置
     * @param enableInsertFlag 为true时，开启插入过滤
     * @param printInsertFlag  为true时，打印出enableInsertFlag标识位在表格中
     * @return 分组后的表格值内容
     * @throws Exception
     */
    private Map<String, List<HashMap>> makeGroups(
            List<HlsDocFileParaTable> tableClomnList,
            List<HashMap> sqlData,
            boolean enableInsertFlag,
            boolean printInsertFlag) throws Exception {
        if (sqlData == null || tableClomnList == null) {
            log.error("参数不可为空!分组失败" );
            return null;
        }
        System.out.println("输入参数有效，开始进行分组" );
        List<String> groupFields = new LinkedList<String>();//使用linkedList保证顺序
        String insertField = "";

        //使用LinkedHashMap保存数据，可能损失一部分性能，但是一般能保证数据的先后顺序 /*new LinkedHashMap<String, List<HashMap>>();*/
        Map<String, List<HashMap>> result = new TreeMap<String, List<HashMap>>(new Comparator<String>() {
            public int compare(String obj1, String obj2) {
                return obj2.compareTo(obj1);
            }
        });

        System.out.println("treeMap初始化完成" );
        Iterator<HlsDocFileParaTable> iterator = tableClomnList.iterator();
        while (iterator.hasNext()) {
            HlsDocFileParaTable map = iterator.next();
            // 开启过滤
            if (enableInsertFlag) {
                String insertFlag = map.getInsertFieldFlag();
                if ("Y".equals(insertFlag)) {
                    insertField = map.getColumnName();
                    System.out.println("找到一个过滤标识：" + insertField);
                }
            }

            String groupFlag = map.getGroupFlag();
            if ("Y".equals(groupFlag)) {
                groupFields.add(map.getColumnName());
                System.out.println("找到一个分组标识：" + map.getColumnName());
                iterator.remove();
            }

        }
        System.out.println("分组标识查找完成,共找到：" + groupFields.size());

        boolean needPrintInsertFlag = false;
        if (enableInsertFlag && printInsertFlag && !insertField.equals("" )) {
            needPrintInsertFlag = true;
            System.out.println("需要输出过滤标识。" );
        }

        // 条件成立则说明没有分组字段，不需要进行分组
        if (groupFields.size() < 1) {

            //不打印过滤字段
            if (!needPrintInsertFlag) {
                for (HashMap map : sqlData) {
                    map.remove(insertField);
                }
            }
            result.put("all" , sqlData);
            System.out.println("不进行分组，返回全部的value。" );
            return result;
        }

        // 根据分组字段进行分组
        for (HashMap map : sqlData) {
            String flag = getGroupFlag(groupFields, map);

            // 判断该数据是否需要插入
            if (enableInsertFlag && !"".equals(insertField)) {
                String insertFieldFlag = map.get(insertField).toString();
                if ("N".equals(insertFieldFlag)) {
                    System.out.println("找到一条需要过滤的数据：" + map.toString());
                    continue;
                }
            }

            //判断是否需要打印过滤字段
            if (!needPrintInsertFlag) {
                map.remove(insertField);
            }


            // 若是结果集中已存在key，则直接添加， 否则需要先创建list，再添加到结果集中
            if (result.containsKey(flag)) {
                System.out.println("已有key：" + flag + "，直接添加" );
                result.get(flag).add(map);
            } else {
                System.out.println("发现新的key：" + flag);
                LinkedList<HashMap> arrayList = new LinkedList<>();
                arrayList.add(map);
                result.put(flag, arrayList);
            }
        }
        System.out.println("分组完成，共有" + result.size() + "组。" );
        return result;
    }

    private String getGroupFlag(List<String> fields, HashMap map) {
        StringBuffer sb = new StringBuffer();
        for (String field : fields) {
            sb.append(map.get(field).toString());
            map.remove(field);
        }
        System.out.println("生成一个分组key：" + sb.toString());
        return sb.toString();
    }


    private static void addBorders(Tbl table, float indwidth, int tablewidth,
                                   int tableBorder) {
        table.setTblPr(new TblPr());
        CTBorder border = new CTBorder();
        border.setColor("auto" );
        border.setSz(new BigInteger(String.valueOf(tableBorder)));
        border.setSpace(new BigInteger("0" ));
        border.setVal(STBorder.SINGLE);

        TblBorders borders = new TblBorders();
        borders.setBottom(border);
        borders.setLeft(border);
        borders.setRight(border);
        borders.setTop(border);
        borders.setInsideH(border);
        borders.setInsideV(border);
        table.getTblPr().setTblBorders(borders);
        if (indwidth != 0) {
            TblWidth w = factory.createTblWidth();
            w.setType("dxa" );
            w.setW(BigInteger.valueOf(UnitsOfMeasurement.mmToTwip(indwidth)));
            table.getTblPr().setTblInd(w);
        }
        if (tablewidth != 0) {
            TblWidth w = factory.createTblWidth();
            w.setType("dxa" );
            w.setW(BigInteger.valueOf(UnitsOfMeasurement.mmToTwip(tablewidth)));
            table.getTblPr().setTblW(w);
        }
    }

}
